package p2pmodule;

import java.net.InetAddress;
import java.util.ArrayList;
import java.util.List;

import com.kenmccrary.jtella.GNUTellaConnection;
import com.kenmccrary.jtella.MessageReceiverAdapter;
import com.kenmccrary.jtella.SearchReplyMessage;
import com.kenmccrary.jtella.SearchSession;

import events.FileFoundEvent;
import events.FileFoundListener;

/** TODO: Should it be an independent thread?. **/
/** TODO: Should it do some stuff when it finished? When it finish?. **/
/**
 * This class will handle the searches of this node into the P2P network.
 * When the GnutellaServant receive a petition to the search() method, it will
 * create an instance of this class and will give it the responsibility of
 * perform the search and receive the QUERYHITs.
 * 
 * This class could be a private class inside the GnutellaServant class, but i
 * decided to get it out in order to make GnutellaServant cleaner.
 * 
 * @author ole
 */
public class Searcher extends MessageReceiverAdapter {
	// Class attributes.
	private String pattern;
	private List<FileFoundListener> ff_listeners = null;
	private GNUTellaConnection conn = null;
	private SearchSession session = null;
	
	/**
	 * Class constructor.
	 * 
	 * @param pattern:
	 * 		The pattern we will search in the Gnutella network.
	 * @param conn:
	 * 		The GNUTellaConnection to use to throw the search.
	 */
	public Searcher(String pattern, GNUTellaConnection conn) {
		this.pattern = pattern;
		this.conn = conn;
		this.ff_listeners = new ArrayList<FileFoundListener>();
	}
	
	/**
	 * Add a new Listener for when a file is found in the Gnutella network.
	 * 
	 * @param listener:
	 * 		The new Listener to be executed when we receive a QUERYHIT. 
	 */
	public void addFileFoundListener(FileFoundListener listener) {
		ff_listeners.add(listener);
	}
	
	/**
	 * Stops this search session even if it wasn't finished.
	 */
	public void finish() {
		session.close();
	}
	
	/**
	 * This method tells the SearchSlave to perform the search to the
	 * Gnutella network.
	 */
	public void performSearch() {
		int query_type, max_results, minimum_speed;
		
		// Create a search session from the connection this servant has to
		// the Gnutella network.
		query_type = minimum_speed = 0;
		max_results = 10;
		session = conn.createSearchSession(pattern, query_type,
										   max_results,
										   minimum_speed, this);
	}
	
	/**
	 * This method handle QUERYHITs messages. For each file found it will
	 * raise a FileFoundEvent. Note that one QUERYHIT can do reference to
	 * more than one file.
	 */
	public void receiveSearchReply(SearchReplyMessage msg) {
		FileFoundEvent event;
		Item item;
		SearchReplyMessage.FileRecord record;
		
		// The reply could have more than one item found.
		for (int i = 0; i < msg.getFileCount(); i++) {
			
			// For each record found create the Item that represents it.
			record = msg.getFileRecord(i);
			try {
				item = new Item(record.getName(),
								record.getSize(),
								InetAddress.getByName(msg.getIPAddress()),
								msg.getPort());
				
				// Raise the event for this Item.
				event = new FileFoundEvent(this, item);
				for (FileFoundListener callback: ff_listeners)
					callback.handleEvent(event);
			}
			catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
}
